package cn.itcast.nems.common.service.impl;

import cn.itcast.nems.common.enumeration.BizNumberTypeEnum;
import cn.itcast.nems.common.service.BizNumberService;
import org.redisson.api.RBloomFilter;
import org.redisson.api.RedissonClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import java.util.concurrent.atomic.AtomicInteger;

/**
 * @author liyong
 * create: 2023-10-27 11:15:44
 **/
@Service
public class BizNumberServiceImpl implements BizNumberService {
    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    // 带日期的单号（可以每日清除数据）
    private final RBloomFilter<String> containDateBloomFilter;

    // 普通的过滤器
    private final RBloomFilter<String> commonBloomFilter;

    // 生成的编号冲突次数
    private final AtomicInteger conflictCount = new AtomicInteger(0);

    public BizNumberServiceImpl(RedissonClient redissonClient) {
        // "NEMS_BIZ_NUMBER_GENERATOR"
        String namePrefix = "nems:biz_number_generator:";
        this.containDateBloomFilter = redissonClient.getBloomFilter(namePrefix + "date");
        // 暂定插入数量: 一百万 、误差率: 千分之一
        this.containDateBloomFilter.tryInit(1000000L, 0.001);
        this.commonBloomFilter = redissonClient.getBloomFilter(namePrefix + "common");
        this.commonBloomFilter.tryInit(1000000L, 0.001);
    }

    @Override
    public String next(BizNumberTypeEnum bizType) {
        RBloomFilter<String> bloomFilter = bizType.isContainDate() ? this.containDateBloomFilter : this.commonBloomFilter;
        return this.next(bizType, bloomFilter);
    }

    @Override
    public Long next() {
        RBloomFilter<String> bloomFilter = this.commonBloomFilter;
        String no = this.next(BizNumberTypeEnum.DEFAULT, bloomFilter);
        return Long.parseLong(no);
    }

    private String next(BizNumberTypeEnum bizType, RBloomFilter<String> bloomFilter) {
        String no;
        boolean isConflict;
        do {
            no = BizNumberGenerator.next(bizType);
            isConflict = bloomFilter.contains(no);
            if (isConflict) {
                final int count = conflictCount.incrementAndGet();
                logger.warn("编号'{}'出现冲突, 至服务启动后出现的总冲突次数:{}", no, count);
            }
        } while (isConflict);
        bloomFilter.add(no);
        return no;
    }
}
